/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.devs.benchmark;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.devs.benchmark.modeling.AtomicType;
import org.devs.benchmark.modeling.ConnectionType;
import org.devs.benchmark.modeling.CoupledType;
import org.devs.benchmark.modeling.InportType;
import org.devs.benchmark.modeling.ModelType;
import org.devs.benchmark.modeling.OutportType;
import org.devs.benchmark.modeling.ParameterType;

/**
 *
 * @author Alejandro Moreno
 */
public class Generator {

    public static AtomicType createAtomic(String name, List<InportType> inports, List<OutportType> outports) {

        AtomicType atomic = new AtomicType(name);

        atomic.getInports((List<InportType>) (new ArrayList<InportType>(inports)).clone());
        atomic.getOutports((List<OutportType>) (new ArrayList<OutportType>(outports)).clone());
        
        return atomic;
    }

    public static AtomicType createAtomic(String name, List<InportType> inports, List<OutportType> outports, List<ParameterType> parameters) {

        AtomicType atomic = new AtomicType(name, parameters);
        
        atomic.getInports((List<InportType>) (new ArrayList<InportType>(inports)).clone());
        atomic.getOutports((List<OutportType>) (new ArrayList<OutportType>(outports)).clone());

        return atomic;
    }

    public static CoupledType createCoupled(String name, ModelType atomic) {

        CoupledType coupled = new CoupledType(name);

        coupled.getModels().add(atomic);

        coupled.getInports((ArrayList<InportType>) ((ArrayList<InportType>) atomic.getInports()).clone());
        coupled.getOutports((ArrayList<OutportType>) ((ArrayList<OutportType>) atomic.getOutports()).clone());

        for (int i = 0; i < coupled.getInports().size(); i++) {
            coupled.getConnections().add(new ConnectionType(coupled, coupled.getInports().get(i), atomic, coupled.getInports().get(i)));
        }

        for (int i = 0; i < coupled.getOutports().size(); i++) {
            coupled.getConnections().add(new ConnectionType(atomic, coupled.getOutports().get(i), coupled, coupled.getOutports().get(i)));
        }

        return coupled;
    }

    public static AtomicType createAtomicLI(String name) {
        return createAtomic(name, Arrays.asList(new InportType("in",int.class)), Arrays.asList(new OutportType("out",int.class)), Arrays.asList(new ParameterType("numDhrystoneDeltExt",1),new ParameterType("numDhrystoneDeltInt",1),new ParameterType("period",0.0)));
    }

    public static AtomicType createAtomicHO(String name) {
        return createAtomic(name, Arrays.asList(new InportType("in1",int.class), new InportType("in2",int.class)), Arrays.asList(new OutportType("out1",int.class), new OutportType("out2",int.class)), Arrays.asList(new ParameterType("numDhrystoneDeltExt",1),new ParameterType("numDhrystoneDeltInt",1),new ParameterType("period",0.0)));
    }

    public static AtomicType createAtomicHOmod(String name) {
        return createAtomic(name, Arrays.asList(new InportType("in1",int.class), new InportType("in2",int.class)), Arrays.asList(new OutportType("out",int.class)), Arrays.asList(new ParameterType("numDhrystoneDeltExt",1),new ParameterType("numDhrystoneDeltInt",1),new ParameterType("period",0.0)));
    }

    public static CoupledType createModelLI(String name, int depth, int width) {

        CoupledType coupled = createCoupled(name + "_Coupled_" + (depth - 1), createAtomicLI(name + "_Atomic_" + (depth - 1) + "_0"));

        for (int j = (depth - 1); j > 0; j--) {

            List<ModelType> models = new ArrayList(width);
            for (int i = (width - 1); i > 0; i--) {
                models.add(createAtomicLI(name + "_Atomic_" + (j - 1) + "_" + (i - 1)));
            }

            models.add(coupled);
            coupled = createCoupledLI(name + "_Coupled_" + (j - 1), models);

        }

        return coupled;

    }

    public static CoupledType createCoupledLI(String name, List<ModelType> models) {

        CoupledType coupled = new CoupledType(name);

        coupled.getModels().addAll(models);

        coupled.getInports((ArrayList<InportType>) ((ArrayList<InportType>) models.get(models.size() - 1).getInports()).clone());
        coupled.getOutports((ArrayList<OutportType>) ((ArrayList<OutportType>) models.get(models.size() - 1).getOutports()).clone());


        for (int j = (models.size() - 1); j >= 0; j--) {
            coupled.getConnections().add(new ConnectionType(coupled, coupled.getInports().get(0), models.get(j), models.get(j).getInports().get(0)));
        }

        coupled.getConnections().add(new ConnectionType(models.get(models.size() - 1), models.get(models.size() - 1).getOutports().get(0), coupled, coupled.getOutports().get(0)));


        return coupled;
    }

    public static CoupledType createModelHI(String name, int depth, int width) {

        CoupledType coupled = createCoupled(name + "_Coupled_" + (depth - 1), createAtomicLI(name + "_Atomic_" + (depth - 1) + "_0"));

        for (int j = (depth - 1); j > 0; j--) {

            List<ModelType> models = new ArrayList(width);
            for (int i = (width - 1); i > 0; i--) {
                models.add(createAtomicLI(name + "_Atomic_" + (j - 1) + "_" + (i - 1)));
            }

            models.add(coupled);
            coupled = createCoupledHI(name + "_Coupled_" + (j - 1), models);

        }

        return coupled;

    }

    public static CoupledType createCoupledHI(String name, List<ModelType> models) {

        CoupledType coupled = createCoupledLI(name, models);

        for (int i = models.size() - 1; i >= 0; i--) {

            if ((i - 1) >= 0) {
                coupled.getConnections().add(new ConnectionType(models.get(i), models.get(i).getOutports().get(0), models.get(i - 1), models.get(i - 1).getInports().get(0)));
            }
        }

        return coupled;
    }

    public static CoupledType createModelHO(String name, int depth, int width) {

        CoupledType coupled = createCoupled(name + "_Coupled_" + (depth - 1), createAtomicHO(name + "_Atomic_" + (depth - 1) + "_0"));

        for (int j = (depth - 1); j > 0; j--) {

            List<ModelType> models = new ArrayList(width);
            for (int i = (width - 1); i > 0; i--) {
                models.add(createAtomicLI(name + "_Atomic_" + (j - 1) + "_" + (i - 1)));
            }

            models.add(coupled);
            coupled = createCoupledHO(name + "_Coupled_" + (j - 1), models);

        }

        return coupled;

    }

    public static CoupledType createCoupledHO(String name, List<ModelType> models) {

        CoupledType coupled = createCoupledHI(name, models);

        coupled.getConnections().add(new ConnectionType(coupled, coupled.getInports().get(1), models.get(models.size() - 2), models.get(models.size() - 2).getInports().get(0)));
        coupled.getConnections().add(new ConnectionType(models.get(models.size() - 2), models.get(models.size() - 2).getOutports().get(0), coupled, coupled.getOutports().get(1)));

        coupled.getConnections().add(new ConnectionType(coupled, coupled.getInports().get(1), models.get(models.size() - 1), models.get(models.size() - 1).getInports().get(1)));
        coupled.getConnections().add(new ConnectionType(models.get(models.size() - 1), models.get(models.size() - 1).getOutports().get(1), coupled, coupled.getOutports().get(1)));

        return coupled;
    }

    public static CoupledType createModelHOmod(String name, int depth, int width) {

        CoupledType coupled = createCoupled(name + "_Coupled_" + (depth - 1), createAtomicHOmod(name + "_Atomic_" + (depth - 1) + "_0"));

        for (int j = (depth - 1); j > 0; j--) {

            Map<ModelType, List<ModelType>> models = new HashMap<ModelType, List<ModelType>>();
            for (int i = (width - 1); i > 0; i--) {

                List<ModelType> modelsM = new ArrayList<ModelType>(i);
                for (int k = i; k > 0; k--) {
                    modelsM.add(createAtomicLI(name + "_Atomic_" + (j - 1) + "_" + (i - 1) + "_" + (k - 1)));
                }

                models.put(createAtomicLI(name + "_Atomic_" + (j - 1) + "_" + (i - 1)), modelsM);
            }

            coupled = createCoupledHOmod(name + "_Coupled_" + (j - 1), models, coupled);

        }

        return coupled;

    }

    public static CoupledType createCoupledHOmod(String name, Map<ModelType, List<ModelType>> models, CoupledType coup) {

        CoupledType coupled = new CoupledType(name);

        coupled.getModels().add(coup);

        coupled.getInports((ArrayList<InportType>) ((ArrayList<InportType>) coup.getInports()).clone());
        coupled.getOutports((ArrayList<OutportType>) ((ArrayList<OutportType>) coup.getOutports()).clone());

        coupled.getConnections().add(new ConnectionType(coupled, coupled.getInports().get(0), coup, coup.getInports().get(0)));
        coupled.getConnections().add(new ConnectionType(coup, coup.getOutports().get(0), coupled, coupled.getOutports().get(0)));

        Iterator<ModelType> itr = models.keySet().iterator();

        while (itr.hasNext()) {
            ModelType model = itr.next();
            coupled.getConnections().add(new ConnectionType(model, model.getOutports().get(0), coup, coup.getInports().get(1)));
            coupled.getModels().add(model);
            List<ModelType> modelsM = models.get(model);
            coupled.getModels().addAll(modelsM);

            coupled.getConnections().add(new ConnectionType(coupled, coupled.getInports().get(1), model, model.getInports().get(0)));
            coupled.getConnections().add(new ConnectionType(coupled, coupled.getInports().get(1), modelsM.get(modelsM.size()-1), modelsM.get(modelsM.size()-1).getInports().get(0)));

            for (int i = modelsM.size()-1; i >0; i--) {
                coupled.getConnections().add(new ConnectionType(modelsM.get(i), modelsM.get(i).getOutports().get(0), modelsM.get(i-1), modelsM.get(i-1).getInports().get(0)));
            }

            ModelType[] modelsA = models.keySet().toArray(new ModelType[models.keySet().size()]);
            for(int i=(modelsA.length-1);i>=0;i--){
                coupled.getConnections().add(new ConnectionType(modelsM.get(0), modelsM.get(0).getOutports().get(0), modelsA[i],modelsA[i].getInports().get(0)));
            }

        }

        return coupled;
    }
}
